package edu.chalmers.targine;

import java.util.concurrent.ConcurrentHashMap;

import org.cocos2d.layers.CCScene;
import org.cocos2d.nodes.CCDirector;
import org.cocos2d.opengl.CCGLSurfaceView;

import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.IntentFilter.MalformedMimeTypeException;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.NfcAdapter.CreateNdefMessageCallback;
import android.nfc.NfcEvent;
import android.os.Bundle;
import android.os.Parcelable;
import android.preference.PreferenceManager;
import android.view.Display;
import edu.dhbw.andar.ARToolkit;
import edu.dhbw.andar.AndARActivity;
import edu.dhbw.andar.exceptions.AndARException;
import edu.dhbw.andar.util.CustomRenderer;
import edu.dhbw.andar.util.Model;

/**
 * 
 * @author Tobijoh & Zezard
 */
public abstract class ARManager extends AndARActivity implements
		ServerConnectionEventListener, CreateNdefMessageCallback,
		NfcAdapter.OnNdefPushCompleteCallback {
	protected ConcurrentHashMap<String, Arbject> arbjects;
	public static ConcurrentHashMap<String, Model> models;
	protected ARToolkit artoolkit;
	protected String LOCAL_MAC;
	protected int WINDOW_WIDTH;
	protected int WINDOW_HEIGHT;

	private NfcAdapter mAdapter;
	private PendingIntent pendingIntent;
	private IntentFilter cardFilter;
	private IntentFilter deviceFilter;
	private IntentFilter[] mIntentFilters;

	protected CCScene cocosScene;
	protected CCGLSurfaceView cocosView;

	protected ServerConnection serverConnection = new ServerConnection();

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		// Store formatted local mac address as device id
		WifiManager wifiMan = (WifiManager) this
				.getSystemService(Context.WIFI_SERVICE);
		WifiInfo wifiInf = wifiMan.getConnectionInfo();
		LOCAL_MAC = wifiInf.getMacAddress();
		LOCAL_MAC = LOCAL_MAC.replace(':', ' ');

		// Store window size
		Display display = getWindowManager().getDefaultDisplay();
		Point size = new Point();
		display.getSize(size);
		WINDOW_WIDTH = size.x;
		WINDOW_HEIGHT = size.y;

		setupIntentFilter();

		CustomRenderer renderer = new CustomRenderer();// optional, may be set
														// to null
		super.setNonARRenderer(renderer);// or might be omitted

		artoolkit = super.getArtoolkit();
		arbjects = new ConcurrentHashMap<String, Arbject>();
		models = new ConcurrentHashMap<String, Model>();

		registerModels();

		SharedPreferences sharedPref = PreferenceManager
				.getDefaultSharedPreferences(this);
		String url = sharedPref.getString(SettingsActivity.URL, "");
		serverConnection.startCommunicatingWithServer(url);
		serverConnection.addEventListener(this);

		cocosView = new CCGLSurfaceView(this);
		cocosView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
		cocosView.getHolder().setFormat(PixelFormat.TRANSLUCENT);

		frame.addView(cocosView, 0);

		startPreview();
	}

	protected abstract void registerModels();

	protected abstract void registerArbject(String arbjectID,
			String patternName, String patternFile) throws AndARException;

	protected abstract void update();

	public void setSharedData(String arbjectID, String var, String value) {
		serverConnection.setSharedData(arbjectID + "." + var, value);
	}

	public abstract void serverConnectionEventOccurred(String[] msg);

	public String[] getSharedData(String key, String var) {
		return serverConnection.getMessage();
	}

	private void setupIntentFilter() {
		mAdapter = NfcAdapter.getDefaultAdapter(this);
		// Register callback to set NDEF message
		mAdapter.setNdefPushMessageCallback(this, this);
		// Register callback to listen for message-sent success
		mAdapter.setOnNdefPushCompleteCallback(this, this);

		pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this,
				getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
		cardFilter = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
		deviceFilter = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
		try {
			// ndef.addDataType("*/*"); /* Handles all MIME based dispatches.
			// You should specify only the ones that you need. */
			// cardFilter.addDataType("*/*");
			deviceFilter.addDataType("text/plain");
			// deviceFilter.addDataType("application/edu.chalmers.gos");
		} catch (MalformedMimeTypeException e) {
			throw new RuntimeException("fail", e);
		}
		mIntentFilters = new IntentFilter[] { deviceFilter, cardFilter };
		// mTagFilters = new IntentFilter[]{cardFilter};
		// mNdefIntentFilters = new IntentFilter[]{cardFilter, deviceFilter};
		// techListsArray = new String[][] { new String[] { NfcF.class.getName()
		// } };
		// intentFiltersArray = new IntentFilter[] {ndef, };
	}

	public NdefMessage createNdefMessage(NfcEvent event) {
		NdefRecord record = new NdefRecord(NdefRecord.TNF_MIME_MEDIA,
				"application/edu.chalmers.gos".getBytes(), new byte[0],
				LOCAL_MAC.getBytes());

		NdefMessage msg = new NdefMessage(new NdefRecord[] { record, // your
																		// data
				NdefRecord.createApplicationRecord(getPackageName()), // the
																		// "application record"
		});

		return msg;
	}

	public void onNdefPushComplete(NfcEvent event) {
		onBeamComplete(event);
	}

	@Override
	public void onResume() {
		super.onResume();
		CCDirector.sharedDirector().resume();
		enableNdefExchangeMode();
	}

	private void enableNdefExchangeMode() {
		if (mAdapter != null && mAdapter.isEnabled()) {
			// Register callback to set NDEF message
			mAdapter.setNdefPushMessageCallback(this, this);
			// Register callback to listen for message-sent success
			mAdapter.setOnNdefPushCompleteCallback(this, this);

			// mAdapter.enableForegroundDispatch(this, pendingIntent,
			// mTagFilters, null);
			// mAdapter.enableForegroundNdefPush(this, msg);
			mAdapter.enableForegroundDispatch(this, pendingIntent,
					mIntentFilters, null);
			// mAdapter.enableForegroundDispatch(this, pendingIntent, null,
			// null);
		}
	}

	@Override
	public void onStart() {
		super.onStart();
		CCDirector.sharedDirector().attachInView(cocosView);
		CCDirector.sharedDirector().setDisplayFPS(true);
		CCDirector.sharedDirector().setAnimationInterval(1.0f / 60.0f);
		cocosScene = GameLayer.scene();
		CCDirector.sharedDirector().runWithScene(cocosScene);
		((GameLayer) (cocosScene.getChildren().get(0))).setARManager(this);
	}

	@Override
	public void onPause() {
		CCDirector.sharedDirector().pause();
		disableNdefExchangeMode();
		super.onPause();
	}

	private void disableNdefExchangeMode() {
		if (mAdapter != null && mAdapter.isEnabled()) {
			mAdapter.disableForegroundNdefPush(this);
			mAdapter.disableForegroundDispatch(this);
		}
	}

	@Override
	public void onStop() {
		CCDirector.sharedDirector().end();
		serverConnection.removeEventListener(this);
		serverConnection.stopCommunicatingWithServer();
		super.onStop();
	}

	@Override
	public void onNewIntent(Intent intent) {
		super.onNewIntent(intent);
		nfcReceived(intent);
	}

	private void nfcReceived(Intent intent) {
		byte[] id = new byte[0];
		String action = intent.getAction();
		Parcelable[] rawMsgs = intent
				.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
		if (rawMsgs != null) {
			NdefMessage msgs = (NdefMessage) rawMsgs[0];
			String msg = new String(msgs.getRecords()[0].getPayload());
			beamReceived(msg);
		} else if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)) {
			id = intent.getByteArrayExtra(NfcAdapter.EXTRA_ID);
			nfcReceived(getArbject(getHex(id)));
		}
	}

	protected abstract void onBeamComplete(NfcEvent event);

	protected abstract void beamReceived(String deviceMac);

	protected abstract void nfcReceived(Arbject arbjcet);

	public abstract void performTapAction(String paraType, Object currentAction);

	protected abstract void accelerometerChanged(float ax, float ay, float az);

	protected String getHex(byte[] bytes) {
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < bytes.length; i++) {
			int b = bytes[i] & 0xff;
			if (b < 0x10) {
				sb.append('0');
			}
			sb.append(Integer.toHexString(b));
			if (i != bytes.length - 1) {
				sb.append(" ");
			}
		}
		return sb.toString();
	}

	public void uncaughtException(Thread thread, Throwable ex) {
	}

	public Arbject getArbject(String arbjectName) {
		synchronized (arbjects) {
			if (arbjects.containsKey(arbjectName)) {
				return (Arbject) arbjects.get(arbjectName);
			} else {
				return null;
			}
		}
	}
}
